<Html>
    <head>
        <title>Razor集成模板引擎</title>
    </head>
    <body>
        <script>
            /*
                Razor 集成    

                         Razor 模板是一个标准的C#类，因此你可以自由使用C#的功能，列入 dependency injection ,使用 Linq、自定义方法，甚至使用Repository.
            */

            /*
                一、安装  Installation  

                          建议使用 ABP命令界面安装此包。


                          1、使用ABP Cli 命令安装

                                    在项目文件夹(.csproj文件所在文件夹)中打开命令床窗口，然后输入以下命令：

                                        abp  add-package  Volo.Abp.TextTemplating.Razor

                                    (如果你尚未安装，首先需要安装ABP命理运行界面。有关其他安装选项，请参阅 软件包说明页面)

                          2、手动安装

                                 如果你想手动安装：

                                  ①、将 "Volo.Abp.TextTemplating.Razor"  NuGet 包添加到你的项目中。

                                         安装  "Volo.Abp.TextTemplating.Razor"包

                                  ②、将 "AbpTextTemplatingRazorModule"添加到模块的依赖项列表中。

                                        [DependsOn(
                                            //...other dependencies
                                            typeof(AbpTextTemplatingRazorModule) //Add the new module dependency
                                            )]
                                        public class YourModule : AbpModule
                                        {
                                        }
             */


            /*
                  二、向CSharpCompilerOptions 添加  MetadataReference （元数据映射）

                            你需要将模板中使用的类型的 MetadataReference （元数据映射）添加到 CSharpCompilerOption's 的 References 中。

                               eg:  
                                    public override void Configure(ServiceConfigurationContext context)
                                    {
                                        // AbpRazorTemplateComilerOptions  (Razor模板编译器选项)
                                        Configure<AbpRazorTemplateComilerOptions>(option=>{
                                            // 通过程序集文件添加对程序集中的所有元数据的引用，(往模板编译器中添加)
                                            options.References.Add(MetadataReference.CreateFromFile(typeof(YourModule).Assembly.Location));    
                                        })
                                    }
            */

            /*
                  三、为模板添加元数据引用

                             你可以向模板添加一些 MetadataReference

                             eg:

                                    public override void ConfigureServices(ServiceCOnfigurationContext context)
                                    {
                                            service.Configure<AbpCompiledViewProviderOptions>(options=>{
                                                // "hello" 是一个文本模板的名称 
                                                
                                                options.TemplateReference.Add("Hello",new List<Assembly>()
                                                {
                                                    Assembly.load("Microsoft.Extensions.Logging.Abstractions"),
                                                    Assembly.load("Microsoft.Extensions.Logging")
                                                }
                                                .Select(x=>MetadataReference.CreateFromFile(x.Loaction)).ToList());
                                            });
                                    }
            */

            /*
                  四、定义模板  Defining Templates

                                        
                            在渲染模板之前，你应该先定义它。创建一个继承自 TemplateDefinitionProvider基类的类：

                             eg： 
                                  public class DemoTemplateDefinitionProvider: TemplateDefinitionProvider
                                  {
                                        public override void Define(ITemplateDefinitionContext context)
                                        {
                                            context.Add(
                                                new  TemplateDefinition("Hello") // 模板名称 "hello"
                                                       .WithRazorEngin()
                                                       .WithVirtualFilePath(
                                                            "/Demos/Hello/Hello.cshtml", // 模板内容路径
                                                            isInlinLocalized:true 
                                                       )
                                            );
                                        }          
                                  }

                                  ①、context:   文本模板定义上下文对象，用于添加新模板或获取依赖模块定义的模板。使用 context.Add(...) 定义新模板

                                  ②、TemplateDefinition类，此类代表一个模板。每个模板都必须有一个唯一的名字(在渲染模板时会用到)

                                  ③、"/Demos/Hello/Hello.cshtml"是模板文件的路径。

                                  ④、isInlineLocalized 用于声明是对所有语言使用单个模板(true),还是对每种语言使用不同模板(false)。
                                     
                                        更多信息请参阅下面的"本地化"部分。

                                  ⑤、WithRenderEngine 方法用于设置模板的渲染引擎。



                            1、模板基类 

                                    每个cshtml 模板页面都需要继承 RazorTemplatePageBase 或者  RazorTemplatePageBase<Model>.

                                    基类中有一些属性可在模板中使用。例如： Localizer、ServiceProvider


                            2、模板内容

                                    WithVirtualFilePath 表示我们正在使用"虚拟文件系统"来存储模板内容。
                                    
                                    ①、在项目中创建一个Hello.cshtml文件，并且在属性窗口将其标记为"嵌入的资源"：

                                            图片： https://raw.githubusercontent.com/abpframework/abp/rel-9.2/docs/en/images/hello-template-razor.png


                                    ②、示例 Hello.cshtml 的内容如下：

                                         // 集成自 RazorTemplatePageBase<Model> 基类
                                         @inherits Volo.Abp.TextTemplating.Razor.RazorTemplatePageBase<HelloModelNamespace.HelloModel>
                                         Hello @Model.Name

                                    ③、HelloModel 类如下：

                                            namespace HelloModelNamespace
                                            {
                                                public class HelloModel
                                                {
                                                    public string Name { get; set; }
                                                }
                                            }

                                    ④、虚拟文件系统需要在 ConfigureServices 方法中添加你文件：

                                            Configure<AbpVirtualFileSystemOptions>(options =>
                                            {
                                                options.FileSets.AddEmbedded<TextTemplateDemoModule>("TextTemplateDemo");
                                            });

                                            TextTemplateDemoModule是用于定义模板的模块类。

                                            TextTemplateDemo是你项目的根命名空间。
            */

            /*
                  五、渲染模板

                            ITemplateRenderer 服务用语呈现模板内容。


                            示例： 渲染简单模板

                                    public class HelloDemo:ITransientDependency
                                    {
                                        private readonly ITemplateRenderer _templateRednderer;

                                        public HelloDemo(ITemplateRenderer templateRenderer)
                                        {
                                            _templateRenderer = templateRenderer;
                                        }

                                        public async Task RunAsync()
                                        {
                                            var result = await _templateRenderer.RenderAsync(
                                                "Hello",  // 模板名称
                                                new HelloModel
                                                {
                                                    Name="John"
                                                }
                                            );

                                            Console.WriteLine(result);
                                        }
                                    }

                            ①、 HelloDemo是一个简单的类，它在狗仔函数中注入 ITemplateRenderer 并在 RunAsync 方法中使用它。
                            
                            ②、RenderAsync 有两个参数：

                                        templateName : 要呈现的模板名称。（在此例中为Hello）

                                        model:  在模板中用作model的对象。 (在此例中为 HelloModel对象)


                            此示例的结果如下：

                                    Hello John :)
            */

            /*
                 六、本地化

                         可以根据当前区域性对模板内容进行本地化。以下各节介绍了两种本地化选项进行本地化：


                                1、内联本地化

                                                            内联本地化使用"本地化系统"对模板内的文本进行本地化。

                                                            eg:  重置密码

                                                                假设你需要给用户发送一封电子邮件来重置器密码。以下是电子邮件/模板的内容：

                                                                ①、模板页面模型类：

                                                                    namespace  ResetMyPasswordNameSpace
                                                                    {
                                                                        public class ResetMyPasswordModel
                                                                        {
                                                                                public stirng Link {get;set;}

                                                                                public string Name {get;set;}
                                                                        }
                                                                    }

                                                                ②、模板页面内容：
                                                                                
                                                                                @inherits Volo.Abp.TextTemplating.Razor.RazorTemplatePageBase<ResetMyPasswordModelNamespace.ResetMyPasswordModel>
                                                                                <a title="@Localizer["ResetMyPasswordTitle"]" href="@Model.Link">@Localizer["ResetMyPassword", Model.Name]</a>、
                                                                
                                                                ③、Localizer 服务与根据当前用户区域性给定的键进行本地化。你需要在本地化文件中定义  "ResetMyPassword" 键：

                                                                                "ResetMyPasswordTitle": "Reset my password",
                                                                                "ResetMyPassword": "Hi {0}, Click here to reset your password"

                                                                ④、你还需要在模板定义提供程序中声明要以给模板一起使用的本地化资源：

                                                                                context.Add()
                                                                                    new  TemplateDefinition(
                                                                                        "PasswordRest",  // 模板名称
                                                                                        typeif(DemoResuource) // 本地化资源类型
                                                                                    )
                                                                                    .WithRazorEngin()
                                                                                    .WithVirtualFilePath(
                                                                                        "/Demos/PasswordReset/PasswordReset.cshtml",   // 模板内容路径
                                                                                        isInlineLoclized:true
                                                                                    )
                                                                                );

                                                                ⑤、就是这样，当你想这样显然这个模板时：

                                                                            var result = await _templateRender.RednderAsync(
                                                                                    "PasswordReseet",   // 模板名称
                                                                                    new PasswordResetModel
                                                                                    {
                                                                                        Name="john",
                                                                                        Link="http://abp.io/example-link?userId=1234$token=ABC"
                                                                                    }
                                                                            );

                                                                ⑥、返回渲染结果(html字符串)
                                                                            
                                                                        <a title="Reset my password" href="https://abp.io/example-link?userId=123&token=ABC">Hi john, Click here to reset your password</a>



                                2、多内容本地化

                                            你可能希望为每种语言创建不同的模板文件，而不是使用本地化系统进行本地化的单个模板。
                                            
                                            如果模板针对特定文化需要完全不同，而不仅仅是简单的文本本地化，那么这可能是必要的。


                                            示例、欢迎邮件模板

                                                  假设你想想用户发送一封欢迎邮件，但希望用户所在文化背景定义一个完全不同的模板》


                                                  ①、首先，创建一个文件夹，并将模板放在里面，例如  en.cshtml  、tr.cshtml  ... 为你指的的每种语言文化各创建一个模板：

                                                         图片：　https://raw.githubusercontent.com/abpframework/abp/rel-9.2/docs/en/images/multiple-file-template-razor.png

                                                　②、然后在模板定义程序类中添加你的模板定义：

                                                         context.Add(
                                                            new  TemplateDefinition(
                                                                name:"WelcomeEmail",
                                                                defultCultureName:"en"  // 默认区域文化名，以便在所需区域没有模板时退回到默认模板
                                                            )
                                                            .WithRazorEngine()
                                                            .WithVirtualFilePath(
                                                                "/Demos/WelcomeEmail/Templatesw",  // 模板内容路径
                                                                isInlineLocalized:false
                                                            )
                                                         );

                                                         <1>、defaultCultureName 默认模板名称，

                                                                    设置"默认模板"，以便在所需区域没有模板时，退回到默认区域。

                                                         <2>、指定"模板文件夹路径"而不是单个模板文件路径

                                                         <3>、在这样情况下把 isInlineLocalized 设置为 false


                                                  ③、正常的渲染模板：
                                                            
                                                            var result = await _templateRenderer.RenderAsync("WelcomeEmail");

                                                            为了简化实例，跳过了此示例的模态框，但你可以按照前面刚刚讲解的那样使用模型。


                                3、指定区域文化渲染模板

                                            ITemplateRenderer 服务如果未指定，则使用当前区域(CultureInfo.CurrentUICulture)。如果需要，可以将区域性指定为 cultureName 参数：

                                                var result = await _templateRenderer.RenderAsync(
                                                      "WelcomeEmal",
                                                      cultureName:"en"
                                                );
                                    
            */


            /*
                 七、布局模板 Layout Templates

                            布局模板用于在其他模板之间创建共享布局。它类似于 app.net core nvc/ Razor Pages中的布局系统。


                            示例：  电子邮件HTML布局模板

                                  例如，你可能希望为所有电子邮件模板创建一个单一的布局。

                                  ①、首先，像之前一样创建一个模板文件：

                                        @inherits Volo.Abp.TextTemplating.Razor.RazorTemplatePageBase
                                        <!DOCTYPE html>
                                        <html lang="en" xmlns="http://www.w3.org/1999/xhtml">
                                        <head>
                                            <meta charset="utf-8" />
                                        </head>
                                        <body>
                                            @Body
                                        </body>
                                        </html>

                                        ★、布局模板必须有一个Body部分，作为呈现的字内容的占位符。

                                 ②、在模板定义程序中添加你的模板：

                                        context.Add(
                                            new TemplateDefinition(
                                                "EmailLayout",
                                                isLayout:true  // 设置为布局模板
                                            )
                                            .WithRazorEngine()
                                            .WithVirtualFilePath(
                                                "/Demo/EmailLayout/EmailLayout.chhtml",
                                                isInlineLocalized:true
                                            )
                                        )


                                  ③、现在，你可以将此模板用作任何其他模板的布局：

                                        context.Add(
                                            new TemplateDefinition(
                                                name:"WelcomeEmail",
                                                defaultCultureName:"en",
                                                layout:"EmialLayout"  // 设置当前模板所使用的布局模板
                                            )
                                            .WithRazorEngine()
                                            .WithVirtualFilePath(
                                                "/Demos/WelcomeEmail/Templates",
                                                isInlineLoacalizaed:false
                                            )
                                        )
            */


            /*
                八、全局上下文

                            ABP传递可用于在模板内访问模型的 model。 如果需要，你可以传递更多全局变量。

                            ①、示例模板内容：

                                        @inherits  Volo.Abp.TextTemplating.Razor.RazorTemplatePageBase

                                        A global object value: @GlobalContext["myGlobalObject"]

                            ②、此模板假定在模板渲染撒花姑娘下文中存在一个 myGlobalObject 对象。你可以按如下所示提供该对象：

                                        var result = await _templateRenderer.RenderAsync(
                                            "GlobalContextUsage",
                                            globalContext:new  Dictionary<string,object>
                                            {
                                                {"myGlobalObject","TEST VALUE"}
                                            }
                                        );

                            ③、渲染结果：

                                        A global object value:  Test Value

            */

            /*
                九、替换现有模板

                            
            */
        </script>
    </body>
</Html>